/*
 * FlashClient - Flash graphical client for Space Creeps
 *
 * Copyright 2008 James W. Hofmann
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

import rules.RulesAPI;

class FlashClient

{

	inline public static var targetframefps = 30;
	inline public static var targetsimfps = 30;
	inline public static var xres = 800;
	inline public static var yres = 600;

	public static var todaysgame : Int = -1;

	public static var frametimer = 
		new flash.utils.Timer(targetframefps,0);
	public static var nettimer = 
		new flash.utils.Timer(100,0); // making this too fast causes
		                      // errors when the queue
		                      // rushes ahead of the connection
	public static var simtimer = new flash.utils.Timer(targetsimfps,0);

	public static var tilemap : GraphicalTileMap;
	public static var background : Background = new Background();

	public static var glowfilter = new flash.filters.GlowFilter
					(0x22FF22, 1, 16, 16);

	public static var button_placetower : SpriteButtonPlaceTower;
	public static var button_placeline : SpriteButtonPlaceLine;
	public static var button_selecttower : SpriteButtonSelectTower;
	public static var button_zoomtoggle : ClickyButton;
	public static var button_fastforward : ClickyButton;
	public static var button_slowdown : ClickyButton;
	public static var button_pause : TogglyButton;
	public static var button_help : TogglyButton;
	public static var button_mainmenu : TogglyButton;

	public static var energycounter = new flash.display.Sprite();
	public static var energytext = new CreepTextField();
	public static var energychangethisframe = 0;

	public static var wavecounter = new flash.display.Sprite();
	public static var wavetext = new CreepTextField();

	public static var gameovertext = new CreepTextField();

	public static var tipdialogue = new SpriteDialogue();
	public static var tipdialoguetext = new CreepTextField();

	public static var lasterror = new CreepTextField();
	public static var lasterrortimer = 0;

	public static var creditsticker = new CreepTextField();
	public static var tickerpos = 0;
	public static var tickerline = 0;

	public static inline var creditstext = 
		["Space Creeps Version 3.1",
		"By James W. Hofmann",
		"This software is licensed under the GNU GPL Version 3.",
		"Code available at code.google.com/p/spacecreeps"];
		
	public static var mainmenu = new MainMenu();

	public static var normalquality : flash.display.StageQuality;
	public static var zoomquality :	flash.display.StageQuality;
	public static var qualitymode : Int;

	public static var sndvol : Float;
	public static var musicon : Bool;

	inline public static var click_placetower = 0;
	inline public static var click_placeline = 1;
	inline public static var click_selecttower = 2;
	inline public static var click_selecttarget = 3;

	public static var clickmode : Int;

	public static var sentrecording : Bool = false;
	public static var playback : Recording = null;
	public static var playingrecording : Bool = false;

	public function new()
	{

		flash.Lib.current.stage.quality = normalquality;
		flash.Lib.current.stage.scaleMode = 
			flash.display.StageScaleMode.EXACT_FIT;
		flash.Lib.current.stage.stageHeight = yres;
		flash.Lib.current.stage.stageWidth = xres;

		background.mouseChildren = false;
		
		simtimer.addEventListener(flash.events.TimerEvent.TIMER, 
									gameloop);
		frametimer.addEventListener(flash.events.TimerEvent.TIMER, 
									frameloop);
		flash.Lib.current.stage.addEventListener(
				flash.events.KeyboardEvent.KEY_DOWN,
				hotkeys);
		background.addEventListener(
				flash.events.MouseEvent.CLICK,
				mapclick);
		flash.Lib.current.stage.addEventListener(
			    flash.events.MouseEvent.MOUSE_WHEEL,
			    mousewheelzoom);

		background.addEventListener(
				flash.events.MouseEvent.MOUSE_MOVE,
				Tips.info_tile);
		
		button_placetower = new SpriteButtonPlaceTower();
		button_placeline = new SpriteButtonPlaceLine();
		button_selecttower = new SpriteButtonSelectTower();
		button_zoomtoggle = new ClickyButton(
						new SpriteButtonZoomToggle());
		button_pause = new TogglyButton(
						new SpriteButtonPause());
		button_fastforward = new ClickyButton(
						new SpriteButtonFastForward());
		button_slowdown = new ClickyButton(
						new SpriteButtonSlowDown());
		button_help = new TogglyButton(
						new SpriteButtonHelp());
		button_mainmenu = new TogglyButton(
						new SpriteButtonMainMenu());

				
		button_placetower.addEventListener(
				flash.events.MouseEvent.MOUSE_OVER,
				Tips.tip_tower, false, 10);		
		button_placetower.addEventListener(
				flash.events.MouseEvent.CLICK,
				set_placetower, false, 10);	
		
		button_placeline.addEventListener(
				flash.events.MouseEvent.MOUSE_OVER,
				Tips.tip_line, false, 10);		
		button_placeline.addEventListener(
				flash.events.MouseEvent.CLICK,
				set_placeline, false, 10);	

		button_selecttower.addEventListener(
				flash.events.MouseEvent.MOUSE_OVER,
				Tips.tip_target, false, 10);		
		button_selecttower.addEventListener(
				flash.events.MouseEvent.CLICK,
				set_selecttower, false, 10);	

		button_zoomtoggle.sprite.addEventListener(
				flash.events.MouseEvent.MOUSE_OVER,
				Tips.tip_zoom, false, 10);		
		button_zoomtoggle.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				zoomtoggle, false, 10);	

		button_pause.sprite.addEventListener(
				flash.events.MouseEvent.MOUSE_OVER,
				Tips.tip_pause, false, 10);		
		button_pause.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				pausegame, false, 10);	

		button_fastforward.sprite.addEventListener(
				flash.events.MouseEvent.MOUSE_OVER,
				Tips.tip_fastforward, false, 10);		
		button_fastforward.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				fastforward, false, 10);
					
		button_slowdown.sprite.addEventListener(
				flash.events.MouseEvent.MOUSE_OVER,
				Tips.tip_slowdown, false, 10);		
		button_slowdown.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				slowdown, false, 10);	

		button_help.sprite.addEventListener(
				flash.events.MouseEvent.MOUSE_OVER,
				Tips.tip_help, false, 10);		
		button_help.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				togglehelp, false, 10);	

		button_mainmenu.sprite.addEventListener(
				flash.events.MouseEvent.MOUSE_OVER,
				Tips.tip_mainmenu, false, 10);		
		button_mainmenu.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				togglemainmenu, false, 10);	

		var odd = false;
		var xoffset = 48.0;

		//// the main hud buttons

		for (b in [button_placetower,button_placeline,
					button_selecttower,button_zoomtoggle.sprite,
					button_slowdown.sprite,
					button_pause.sprite, 
					button_fastforward.sprite,
					button_help.sprite,
					button_mainmenu.sprite
					])
		{
			flash.Lib.current.addChild(b);
			b.gotoAndStop("unpressed");
		
			if (!odd)
				b.y = yres*9/13;
			else
				b.y = yres*9/13 + button_placeline.height/2;
			odd = (!odd);
			b.x = xoffset;
			xoffset+=button_placeline.width * 3/4;

		}

		button_mainmenu.sprite.x+=25; // offset this one a little more

		button_pause.click();


		set_placetower();

		var typicaltextformat = new flash.text.TextFormat();
		typicaltextformat.size = 48;
		typicaltextformat.font = "Vera Bold";
		typicaltextformat.color = 0xFFFFFF;

		var smalltextformat = new flash.text.TextFormat();
		smalltextformat.size = 6.5;
		smalltextformat.font = "Vera Bold";
		smalltextformat.color = 0xFFFFFF;

		var centertextformat = new flash.text.TextFormat();
		typicaltextformat.size = 48;
		typicaltextformat.font = "Vera Bold";
		centertextformat.align = flash.text.TextFormatAlign.CENTER;
		typicaltextformat.color = 0xFFFFFF;

		var rightaligntextformat = new flash.text.TextFormat();
		rightaligntextformat.size = 48;
		rightaligntextformat.font = "Vera Bold";
		rightaligntextformat.align = flash.text.TextFormatAlign.RIGHT;
		rightaligntextformat.color = 0xFFFFFF;

		var gameovertextformat = new flash.text.TextFormat();
		gameovertextformat.size = 80;
		gameovertextformat.font = "Vera Bold";
		gameovertextformat.color = 0xFFFFFF;

		var shadowfilter : flash.filters.DropShadowFilter = 
			new flash.filters.DropShadowFilter();
		
		for (t in [energytext,wavetext, lasterror, gameovertext,
					tipdialoguetext, creditsticker])
		{
			t.defaultTextFormat = typicaltextformat;
			t.selectable = false;
			t.width = xres;
			t.mouseEnabled = false;
			t.tabEnabled = false;
			t.filters = [shadowfilter];
		}

		lasterror.defaultTextFormat = centertextformat;
		lasterror.autoSize = flash.text.TextFieldAutoSize.CENTER;

		gameovertext.defaultTextFormat = gameovertextformat;
		gameovertext.visible = false;
		gameovertext.autoSize = flash.text.TextFieldAutoSize.CENTER;
		gameovertext.x = xres/2 - gameovertext.width/2;
		gameovertext.y = yres/2 - yres/8;
		gameovertext.text = "G A M E   O V E R";

		wavetext.defaultTextFormat = rightaligntextformat;

		creditsticker.autoSize = flash.text.TextFieldAutoSize.CENTER;
		creditsticker.filters = [];
		
		//note the "counter" sprites are variations of header.svg
		//the whole thing is a composite built at runtime

		var energycounterspr = new SpriteEnergyCounter();
		energycounter.addChild(energycounterspr);
		energycounterspr.cacheAsBitmap = true;
		var energycounterenergysprite = new SpriteEnergy();
		energycounterenergysprite.cacheAsBitmap = true;
		energycounter.addChild(energycounterenergysprite);
		energycounterenergysprite.x=1;
		energycounterenergysprite.y=8;
		energycounter.addChild(energytext);
		energytext.x = 100;
		energytext.y = 20;
		energycounter.scaleX = 0.5;
		energycounter.scaleY = 0.5;
		flash.Lib.current.addChild(energycounter);

		var wavecounterspr = new SpriteWaveCounter();
		wavecounterspr.cacheAsBitmap = true;
		wavecounter.addChild(wavecounterspr);
		wavecounter.addChild(wavetext);
		wavecounterspr.x = xres*2;
		wavetext.width = xres;
		wavetext.x = xres-8;
		wavetext.y = 20;
		wavecounter.scaleX = 0.5;
		wavecounter.scaleY = 0.5;
		flash.Lib.current.addChild(wavecounter);

		flash.Lib.current.addChild(tipdialogue);
		tipdialogue.scaleX = 3;
		tipdialogue.scaleY = 2.5;
		tipdialogue.y = Tips.tip_y;
		tipdialogue.x = Tips.tip_leftx;

		tipdialoguetext.antiAliasType = flash.text.AntiAliasType.ADVANCED;
		tipdialoguetext.defaultTextFormat = smalltextformat;
		tipdialoguetext.width = 
			((tipdialogue.width)/tipdialogue.scaleX)-12;
		tipdialoguetext.height = 
			(tipdialogue.height)/tipdialogue.scaleY;
		tipdialoguetext.multiline = true;
		tipdialoguetext.wordWrap = true;

		tipdialogue.addChild(tipdialoguetext);
		tipdialoguetext.x = 5;
		tipdialoguetext.y = 8;

		flash.Lib.current.addChild(lasterror);
		lasterror.y += 100;

		flash.Lib.current.addChild(gameovertext);

		flash.Lib.current.addChild(mainmenu);

		flash.Lib.current.addChild(creditsticker);

		creditsticker.x = xres;
		creditsticker.y = yres-60;
		creditsticker.visible = false;
		
		var uielements = [
			button_placetower,button_placeline,
			button_selecttower,button_zoomtoggle.sprite,
			button_slowdown.sprite,
			button_pause.sprite, 
			button_fastforward.sprite,
			button_help.sprite,
			button_mainmenu.sprite,
			tipdialogue, tipdialoguetext, lasterror,
			energycounter, energytext
		];

		for (ui in uielements)
		{
			setui(ui);
		}

		button_help.click();
		button_mainmenu.click();

		for (n in [tilemap.spr, tipdialogue, tipdialoguetext,
				button_fastforward.sprite,
				button_help.sprite,
				button_mainmenu.sprite,
				button_pause.sprite,
				button_placeline,
				button_placetower,
				button_selecttower,
				button_slowdown.sprite,
				button_zoomtoggle.sprite,
				energycounter,
				energytext,
				wavecounter]
			) // toggle off for initial load
		{
			n.visible = false;
		}
		
		FlashNetworkClient.start();

		frametimer.start();
		
		net_on_startup();
	}

	public static function net_on_startup()
	{

		FlashNetworkClient.downloadnumber.request();

		/// if the page I'm loaded from includes this stuff,
		/// also start a recording playback!

		var playrecording : Int;

		var params : Dynamic = flash.Lib.current.root.loaderInfo.parameters;

		try
		{
			playrecording = params.rec;
		}
		catch(d : Dynamic) {playrecording=-1;}
	
		if (playrecording>0)
		{
			
			FlashNetworkClient.gametodownload = playrecording;		
			
			FlashNetworkClient.downloadgame.request();

		}		
		
	}

	public static function setui(ui : Dynamic)
	{
		try{
			ui.mouseChildren = false;
			ui.tabChildren = false;			
			}
		catch (d: Dynamic) {}
		ui.doubleClickEnabled = false;			
		ui.tabEnabled = false;			
		ui.cacheAsBitmap = true;					
	}

	public static function rollcredits()
	{
		if (creditsticker.visible == false)
		{
		tickerpos = 0;
		tickerline = 0;
		creditsticker.visible = true;
		gencreditstext();
		updatecredits();
		}
	}

	public static inline function gencreditstext()
	{
		creditsticker.text = creditstext[tickerline];
		tickerline = tickerline+1;
	}

	public static inline function updatecredits()
	{
		if (creditsticker.visible == true)
		{
			var totaldist = creditsticker.width+xres;
			if (tickerpos>=totaldist) 
			{
				if (tickerline<creditstext.length)
				{
					gencreditstext();
					tickerpos=0;
				}
				else
					creditsticker.visible = false;
			}
			else
			{
				tickerpos+=5;
			}
			var cur = xres-tickerpos;
			creditsticker.x = cur;
		}		
	}

	public static function main()
	{		
		SiteSpecific.start();
		flash.Lib.current.addChild(background);
		restartgame();
		new FlashClient();
		
			SFX.clearsounds();
		
	}

	public static function restartgame(?seed : Int, ?recording : Recording)
	{

		sentrecording = false;
		
		try // kill old maps
		{
			tilemap.spr.removeEventListener(
				flash.events.MouseEvent.CLICK,mapclick);
			tilemap.spr.removeEventListener(
				flash.events.MouseEvent.MOUSE_MOVE,Tips.info_tile);		
			flash.Lib.current.stage.removeEventListener(
			    flash.events.Event.MOUSE_LEAVE,tilemap.mouseLeave);
			flash.Lib.current.removeChild(tilemap.spr);				
			// set up the interface "just so"
			if (!button_pause.on)
				pausegame();
			if (mainmenu.container.visible)
				togglemainmenu();
			for (n in [tilemap.spr,
				button_fastforward.sprite,
				button_help.sprite,
				button_mainmenu.sprite,
				button_pause.sprite,
				button_placeline,
				button_placetower,
				button_selecttower,
				button_slowdown.sprite,
				button_zoomtoggle.sprite,
				energycounter,
				energytext,
				wavecounter])
			{
				n.visible = true;
			}
			if (button_help.on==true)
			{
				tipdialogue.visible=true;
				tipdialoguetext.visible=true;				
			}
					
		}
		catch (a : Dynamic) {}
		
		tilemap = new GraphicalTileMap();

		tilemap.spr.addEventListener(
				flash.events.MouseEvent.CLICK,
				mapclick);
		tilemap.spr.addEventListener(
				flash.events.MouseEvent.MOUSE_MOVE,
				Tips.info_tile);		

		flash.Lib.current.addChild(tilemap.spr);				

		flash.Lib.current.setChildIndex(background,0);
		flash.Lib.current.setChildIndex(tilemap.spr,1);

		flash.Lib.current.stage.addEventListener(
			    flash.events.Event.MOUSE_LEAVE,
			    tilemap.mouseLeave);

		gameovertext.visible = false;

		if (seed!=-1)
		{
			if (seed==FlashClient.todaysgame)
			{
				FlashClient.playingrecording = false;
				rules.RulesAPI.startgame(seed, true);
			}
			else // custom game
			{
				FlashClient.playingrecording = false;
				rules.RulesAPI.startgame(seed, false);
			}
		}
		else if (recording!=null)
		{
			FlashClient.playback = recording;
			FlashClient.playingrecording = true;
			rules.RulesAPI.startgame(recording.seed, false);
		}
		else
		{
			FlashClient.playingrecording = false;
			rules.RulesAPI.startgame(null, true);
		}

		tilemap.tilemapgen();

		for (r in rules.GameResponse.composition)
		 	parseresponse(r);		

		SFX.clearsounds();
		 
	}

	public static function togglemainmenu(?e: flash.events.MouseEvent)
	{
		button_mainmenu.click();
		if (button_mainmenu.on)
		{
			mainmenu.visible = true;
		}
		else
		{
			mainmenu.visible = false;			
		}
	}

	public static function pausegame(?e: flash.events.MouseEvent)
	{
		if (RulesAPI.g.gameover==false)
		{
			button_pause.click();
			if (simtimer.running)
			{
				simtimer.stop();
			}
			else
			{
				simtimer.start();
			}
		}
	}

	public static function togglehelp(?e: flash.events.MouseEvent)
	{
			button_help.click();
			if (!button_help.on)
			{
				tipdialogue.visible = false;
				tipdialoguetext.visible = false;
			setmsgdisplay("Tips off");		
			}
			else
			{
				tipdialogue.visible = true;
				tipdialoguetext.visible = true;
			setmsgdisplay("Tips on");		
			}
	}

	public static function fastforward(?e: flash.events.MouseEvent)
	{
		button_fastforward.click();
		if (simtimer.delay>5)
			simtimer.delay-=5;
		else if (simtimer.delay>1)
			simtimer.delay-=1;
		setmsgdisplay("Clock now runs at "+simtimer.delay+"ms");		
	}

	public static function slowdown(?e: flash.events.MouseEvent)
	{
		button_slowdown.click();
		if (simtimer.delay>=5)
			simtimer.delay+=5;
		else simtimer.delay+=1;
		setmsgdisplay("Clock now runs at "+simtimer.delay+"ms");		
	}

	public static function zoomtoggle(?e: flash.events.MouseEvent)
	{
		var tm = FlashClient.tilemap;
			if (tm.destscalesize==0.2)
			{
				tm.destscalesize=0.8;
			}
			else if (tm.destscalesize==0.8)
			{
				tm.destscalesize=0.4;
			}
			else
			{
				tm.destscalesize=0.2;
			}
		button_zoomtoggle.click();		
		tm.upcam();		
	}

	public static function mousewheelzoom(e : flash.events.MouseEvent)
	{
		var tm = FlashClient.tilemap;
		if (e.delta>0)
		{
			tm.destscalesize=Math.min(2, tm.destscalesize+0.05);
		}
		else
		{
			tm.destscalesize=Math.max(0.1, tm.destscalesize-0.05);			
		}
		tm.upcam();
	}

	public static function hotkeys(e : flash.events.KeyboardEvent)
	{
		var char = Std.chr(e.charCode);
		var tm = FlashClient.tilemap;
		if (tilemap.spr.visible==true) // on first run, we "aren't
									   // playing yet"
		{
			if (char=="1") // place towers
			{
				set_placetower();
			}
			else if (char=="2") // place lines
			{
				set_placeline();
			}
			else if (char=="3") // select tower
			{
				set_selecttower();
			}
			else if (char=="4") // preset zoom levels
			{
				zoomtoggle();
			}
			else if (char=="5") // slowdown
			{
				slowdown();
			}
			else if (char=="6") // pause
			{
				pausegame();
			}
			else if (char=="7") // fastforward
			{
				fastforward();
			}
			else if (char=="8") // help
			{
				togglehelp();
			}
			else if (char=="9") // mainmenu
			{
				togglemainmenu();
			}
			
			if (e.keyCode==38) // cam up
			{
				tm.setcam(tm.camtx,tm.camty-1);
			}
			if (e.keyCode==39) // cam right
			{
				tm.setcam(tm.camtx+1,tm.camty);
			}
			if (e.keyCode==37) // cam left
			{
				tm.setcam(tm.camtx-1,tm.camty);			
			}
			if (e.keyCode==40) // cam down
			{
				tm.setcam(tm.camtx,tm.camty+1);			
			}
		}
	}

	public static function set_placetower
		(?e : Dynamic)
	{

		SFX.interfacehi = true;

		tilemap.targets.visible = false;
		button_placetower.gotoAndStop("pressed");
		button_placetower.filters = [glowfilter];
		button_placeline.gotoAndStop("unpressed");
		button_placeline.filters = [];
		button_selecttower.gotoAndStop("unpressed");		
		button_selecttower.filters = [];
		clickmode = click_placetower;

	}

	public static function set_placeline
		(?e : Dynamic)
	{

		SFX.interfacehi = true;

		tilemap.targets.visible = false;
		button_placetower.gotoAndStop("unpressed");
		button_placetower.filters = [];
		button_placeline.gotoAndStop("pressed");
		button_placeline.filters = [glowfilter];
		button_selecttower.gotoAndStop("unpressed");		
		button_selecttower.filters = [];
		clickmode = click_placeline;

	}

	public static function set_selecttower
		(?e : Dynamic)
	{

		SFX.interfacehi = true;

		tilemap.targets.visible = true;
		tilemap.targets.cursorattachedtarget.visible = false;
		button_placetower.gotoAndStop("unpressed");
		button_placetower.filters = [];
		button_placeline.gotoAndStop("unpressed");
		button_placeline.filters = [];
		button_selecttower.gotoAndStop("pressed");		
		button_selecttower.filters = [glowfilter];
		clickmode = click_selecttower;

	}

	public static function set_selecttarget
		(?e : Dynamic)
	{

		tilemap.targets.visible = true;
		tilemap.targets.cursorattachedtarget.visible = true;
		button_placetower.gotoAndStop("unpressed");
		button_placeline.gotoAndStop("unpressed");
		button_selecttower.gotoAndStop("unpressed");		
		clickmode = click_selecttarget;

	}

	public static function mapclick(e : flash.events.MouseEvent)
	{
		var testpt = FlashClient.tilemap.screencoordtogamecoord(
			flash.Lib.current.stage.mouseX,
			flash.Lib.current.stage.mouseY);
			
		if(rules.GameMap.inbounds(testpt.x,testpt.y))
		{
			var clicktile = RulesAPI.g.m.tiles[testpt.x][testpt.y];

			var newmessage : rules.GameMessage = null;

			if (clickmode==click_placetower)
			{		
			newmessage = new rules.GameMessage(
						rules.GameMessage.msg_placetower, [testpt.x,
														   testpt.y]);
			}
			else if (clickmode==click_placeline)
			{
			newmessage = new rules.GameMessage(
						rules.GameMessage.msg_placeline, [testpt.x,
														   testpt.y]);			
			}
			else if (clickmode==click_selecttower)
			{
			newmessage = new rules.GameMessage(
						rules.GameMessage.msg_selecttower, [testpt.x,
														   testpt.y]);
			}
			else if (clickmode==click_selecttarget)
			{
			newmessage = new rules.GameMessage(
						rules.GameMessage.msg_selecttarget, [testpt.x,
														   testpt.y]);
			}

			
			try
				{var responses = RulesAPI.message(
					[newmessage]);
				 for (r in responses)
				 parseresponse(r);
				if (playingrecording)
				{
					playingrecording=false;
					setmsgdisplay("Recording playback stopped");
				}
			}
			catch (z : rules.GameResponse)
			{
				setmsgdisplay(z.message);
				if (playingrecording)
				{
					playingrecording=false;
					setmsgdisplay("Recording playback stopped");
				}
			 }
			catch (z : Dynamic)
			{
			 trace("****"+Std.string(z));	
			}

		}
	}
	
	public static function setmsgdisplay(text : String, ?timer : Int)
	{
		if (timer==null)
			timer = 90;
		 lasterror.text = text;
		 lasterrortimer = timer;
	}
	
	public static function gameloop(e : flash.events.TimerEvent)
	{
		energychangethisframe = 0;
		if (playingrecording)
		{
			// goal: play all messages until the next runsim.
			var messagestoplay = new Array<rules.GameMessage>();
			var next : rules.GameMessage = FlashClient.playback.record.pop();

			// parcel out long runsim messages one at a time, otherwise
			// the playback will "jump" from beginning to end in an
			// instant

			if (next.style==rules.GameMessage.msg_runsim 
				&& next.message>1)
			{
				FlashClient.playback.record.push(new rules.GameMessage(
					rules.GameMessage.msg_runsim, next.message-1));
				next.message = 1;
			}

			messagestoplay.push(next);

			// now grab all the messages needed for this frame.

			try
			{
				while ((FlashClient.playback.record.length>0))
				{
					next = FlashClient.playback.record.last();
					if (next.style==rules.GameMessage.msg_runsim)
						break;
					next = FlashClient.playback.record.pop();
					messagestoplay.push(next);
				}
			}
			catch (d : Dynamic) // if the data's bad ignore it!
			{messagestoplay = [new rules.GameMessage(
			rules.GameMessage.msg_runsim, 1)];} 

			// now parse it.

			try
				{var responses = RulesAPI.message(
					messagestoplay);
			 for (r in responses)
				parseresponse(r);
			}
			catch (z : rules.GameResponse)
			{
			 trace(z.message);
			 }

			// and stop the recording if it's done.

			if (messagestoplay.length==0)
				playingrecording=false;			
		}
		else
		{
			try
				{var responses = RulesAPI.message(
					[new rules.GameMessage(
						rules.GameMessage.msg_runsim, 1)]);
				 for (r in responses)
					parseresponse(r);
					}
			catch (z : rules.GameResponse)
			{
			 trace(z.message);
			 }
						
		}

		if (RulesAPI.g.gameover)
		{
			if (RulesAPI.amrecording && sentrecording==false)
			{
				mainmenu.switchtosubmit();
				if (mainmenu.visible == false)
					button_mainmenu.click();
				mainmenu.visible = true;
			}			
		}

	}
	
	public static function frameloop(e : flash.events.TimerEvent)
	{		

		var egy : String;
		if (energychangethisframe>0)
			egy = " (+"+energychangethisframe;
		else
			egy = " ("+energychangethisframe;

		energytext.text = rules.RulesAPI.g.energy+egy+")";

		wavetext.text = "Wave " + rules.RulesAPI.g.wavenum + 
			" (Next "+ rules.RulesAPI.g.timetonextwave + ")" ;
		
		tilemap.picktile(flash.Lib.current.stage.mouseX,
				flash.Lib.current.stage.mouseY);

		lasterrortimer -= 1;
		if (lasterrortimer<=0)
		{
			lasterror.visible = false;
			lasterrortimer = 0;
		}
		else
		{
			lasterror.visible = true;			
		}

		if (RulesAPI.g.gameover)
		{
			gameovertext.visible = true;
		}

		button_zoomtoggle.up();		
		button_fastforward.up();		
		button_slowdown.up();		

		mainmenu.buttoncredits.up();		
		mainmenu.buttondailygame.up();		
		mainmenu.buttoncustomgame.up();		
		mainmenu.buttonhighscores.up();		
		mainmenu.buttonhs_titlemenu.up();		
		mainmenu.buttonsubmit_titlemenu.up();		
		mainmenu.buttonsubmit_submit.up();		

		Tips.up();

		tilemap.upcam();
		
		updatecredits();
		
		SFX.up();
		
	}

	public static function parseresponse(r : rules.GameResponse)
	{
		switch(r.style)
		{
			default:
				try {}//trace(r.message);} 
				catch(x : Dynamic){}
			case(rules.GameResponse.msg_gameover):
				simtimer.stop();
				button_pause.on = false;
				button_pause.click();
				SFX.interfacehi = false;
				SFX.gameover = true;
			
			case(rules.GameResponse.msg_energychange):
				energychangethisframe += r.message;
			case(rules.GameResponse.msg_fortadd):
				SFX.placement = true;
				var data = r.message[0];
				tilemap.forts.addfort(data,r.message[1]);
				if (data.shooter!=null)
					tilemap.targets.add(data.shooter);
			case(rules.GameResponse.msg_shooterupgrade):
				tilemap.targets.update(r.message);
			case(rules.GameResponse.msg_shooterdowngrade):
				tilemap.targets.update(r.message);
			case(rules.GameResponse.msg_fortdamage):
				tilemap.forts.healthupdate(r.message[0]);
			case(rules.GameResponse.msg_fortrepair):
				tilemap.forts.healthupdate(r.message);
			case(rules.GameResponse.msg_fortkill):
				SFX.fortdeath = true;
				tilemap.forts.killfort(r.message);
				if (r.message.shooter!=null)
					tilemap.targets.remove(r.message.shooter);
			
			case(rules.GameResponse.msg_creepadd):
				tilemap.creeps.addcreep(r.message[0],r.message[1]);
			case(rules.GameResponse.msg_creephurt):
				tilemap.creeps.healthupdate(r.message[0]);
			case(rules.GameResponse.msg_creepkill):
				SFX.creepdeath = true;
				tilemap.creeps.killcreep(r.message);
			case(rules.GameResponse.msg_creepmove):
				tilemap.creeps.move(r.message[0], r.message[1]);

			case(rules.GameResponse.msg_newwave):
				SFX.nextwave = true;
				tilemap.waves.addwave(r.message);				
			case(rules.GameResponse.msg_waveover):
				tilemap.waves.endwave(r.message);

			case(rules.GameResponse.msg_shotadd):
				tilemap.shots.addshot(r.message);				
			case(rules.GameResponse.msg_shotmovement):
				tilemap.shots.move(r.message[0],r.message[1]);
			case(rules.GameResponse.msg_shotdead):
				tilemap.shots.killshot(r.message);

			case(rules.GameResponse.msg_towerselected):
				SFX.interfacelo = true;
				tilemap.targets.drawrangehex(r.message);
				set_selecttarget();
			case(rules.GameResponse.msg_towertargetchange):
				SFX.interfacehi = true;
				tilemap.targets.update(r.message);
				clickmode = click_selecttower;
				set_selecttower();

			case(rules.GameResponse.msg_shooterfired):
				tilemap.forts.rotatefort(r.message[0],
											   r.message[1]);

		}
	}
	
}

class MainMenu extends flash.display.Sprite
{

	public var container : SpriteHex;
	public var buttondailygame : ClickyButton;
	public var buttoncustomgame : ClickyButton;
	public var buttoncredits : ClickyButton;
	public var buttonhighscores : ClickyButton;

	public var buttonhs_titlemenu : ClickyButton;
	public var buttonsubmit_titlemenu : ClickyButton;
	
	public var customgameinput : Array<flash.text.TextField>;

	public var buttonsubmit_submit : ClickyButton;
	public var nametext_submit : flash.text.TextField;
	public var grouptext_submit : flash.text.TextField;

	public var framessurvived : CreepTextField;

	public var selectquality : CreepTextField;
	public var selectsound : CreepTextField;
	public var selectmusic : CreepTextField;

    // MainMenu contains three menus: the title, high scores,
    //                                and score submit 
    // They share the container, but nothing else.
    
    /////// for the current release high scores has been put on hold.
    /////// instead it links to spacecreeps.com, which will have a PHP
    /////// high score system.

	/////// I want to rework the interface before doing score views
	/////// in the client.
    
	public var titlemenu : flash.display.Sprite;
	public var highscoremenu : flash.display.Sprite;
	public var submitmenu : flash.display.Sprite;
	
	public var settings : flash.net.SharedObject;
	
	public function new()
	{

		super();

		container = new SpriteHex();
		container.scaleX = 7;
		container.scaleY = 7;
		container.alpha = 0.7;

		buttondailygame = new ClickyButton(new SpriteButtonBlank());
		buttoncustomgame = new ClickyButton(new SpriteButtonBlank());
		buttoncredits = new ClickyButton(new SpriteButtonBlank());
		buttonhighscores = new ClickyButton(new SpriteButtonBlank());
		buttonhs_titlemenu = new ClickyButton(new SpriteButtonBlank());
		buttonsubmit_titlemenu = new ClickyButton(new SpriteButtonBlank());
		buttonsubmit_submit = new ClickyButton(new SpriteButtonBlank());

		buttondailygame.sprite.alpha = 0.7;
		buttoncustomgame.sprite.alpha = 0.7;
		buttoncredits.sprite.alpha = 0.7;
		buttonhighscores.sprite.alpha = 0.7;
		buttonhs_titlemenu.sprite.alpha = 0.7;
		buttonsubmit_titlemenu.sprite.alpha = 0.7;
		buttonsubmit_submit.sprite.alpha = 0.7;

		this.addChild(container);

		titlemenu = new flash.display.Sprite();
		highscoremenu = new flash.display.Sprite();
		submitmenu = new flash.display.Sprite();

		this.addChild(titlemenu);
		this.addChild(highscoremenu);
		this.addChild(submitmenu);

		titlemenu.addChild(buttondailygame.sprite);
		titlemenu.addChild(buttonhighscores.sprite);
		titlemenu.addChild(buttoncustomgame.sprite);
		titlemenu.addChild(buttoncredits.sprite);

		highscoremenu.addChild(buttonhs_titlemenu.sprite);
		
		submitmenu.addChild(buttonsubmit_titlemenu.sprite);
		submitmenu.addChild(buttonsubmit_submit.sprite);

		container.x = FlashClient.xres/2 - container.width/2;
		container.y = FlashClient.yres/2 - container.height/2;
		
		buttondailygame.sprite.x = FlashClient.xres/2 - 300;
		buttondailygame.sprite.y = FlashClient.yres/2-
			buttondailygame.sprite.height/2;

		buttonhighscores.sprite.x = FlashClient.xres/2 + 159;
		buttonhighscores.sprite.y = FlashClient.yres/2 + 72-
			buttonhighscores.sprite.height/2;

		buttoncustomgame.sprite.x = FlashClient.xres/2 - 217;
		buttoncustomgame.sprite.y = FlashClient.yres/2 + 144-
			buttoncustomgame.sprite.height/2;

		buttoncredits.sprite.x = FlashClient.xres/2 + 76;
		buttoncredits.sprite.y = FlashClient.yres/2 + 216-
			buttoncredits.sprite.height/2;

		buttonhs_titlemenu.sprite.x = FlashClient.xres/2 + 76;
		buttonhs_titlemenu.sprite.y = FlashClient.yres/2 + 216 -
			buttonhs_titlemenu.sprite.height/2;

		buttonsubmit_titlemenu.sprite.x = FlashClient.xres/2 + 76;
		buttonsubmit_titlemenu.sprite.y = FlashClient.yres/2 + 216 -
			buttonsubmit_titlemenu.sprite.height/2;

		buttonsubmit_submit.sprite.x = FlashClient.xres/2 - 217;
		buttonsubmit_submit.sprite.y = FlashClient.yres/2 + 144-
			buttonsubmit_submit.sprite.height/2;

		var dailygametext = new CreepTextField();
		var customgametext = new CreepTextField();
		var creditstext = new CreepTextField();
		var highscorestext = new CreepTextField();
		var back_hs = new CreepTextField();
		var back_submit = new CreepTextField();
		var submit_submit = new CreepTextField();
		var header_submit = new CreepTextField();

		var nametext = new CreepTextField();
		var grouptext = new CreepTextField();
		nametext_submit = new CreepTextField();
		grouptext_submit = new CreepTextField();

		selectmusic = new CreepTextField();
		selectsound = new CreepTextField();
		selectquality = new CreepTextField();

		framessurvived = new CreepTextField();

		var menuformat = new flash.text.TextFormat();
		menuformat.size = 36;
		menuformat.font = "Vera Bold";
		menuformat.color = 0xFFFFFF;

		var centermenuformat = new flash.text.TextFormat();
		centermenuformat.size = 36;
		centermenuformat.font = "Vera Bold";
		centermenuformat.color = 0xFFFFFF;
		centermenuformat.align = flash.text.TextFormatAlign.CENTER;
		centermenuformat.blockIndent = null;
		centermenuformat.indent = null;

		var selectformat = new flash.text.TextFormat();
		selectformat.size = 25;
		selectformat.font = "Vera Bold";
		selectformat.color = 0xFFCCCC;

		var headerformat = new flash.text.TextFormat();
		headerformat.size = 52;
		headerformat.font = "Vera Bold";
		headerformat.color = 0xFFFFFF;

		var inputformat = new flash.text.TextFormat();
		inputformat.size = 28;
		inputformat.font = "Vera Bold";
		inputformat.color = 0xFFCCCC;
		inputformat.align = flash.text.TextFormatAlign.CENTER;
		inputformat.blockIndent = null;
		inputformat.indent = null;

		var shadowfilter : flash.filters.DropShadowFilter = 
			new flash.filters.DropShadowFilter();
		
		for (t in [dailygametext,customgametext,creditstext,
				   highscorestext,back_hs,back_submit,
				   nametext_submit, grouptext_submit,
				   submit_submit, nametext, grouptext, header_submit,
				   selectsound,selectmusic,selectquality,
				   framessurvived])
		{
			t.defaultTextFormat = menuformat;
			t.selectable = false;
			t.autoSize = flash.text.TextFieldAutoSize.RIGHT;
			t.mouseEnabled = false;
			t.tabEnabled = false;
			t.filters = [shadowfilter];
		}

		header_submit.defaultTextFormat = headerformat;

		selectsound.defaultTextFormat = selectformat;
		selectmusic.defaultTextFormat = selectformat;
		selectquality.defaultTextFormat = selectformat;
		selectsound.mouseEnabled = true;
		selectmusic.mouseEnabled = true;
		selectquality.mouseEnabled = true;

		nametext_submit.selectable = true;
		nametext_submit.mouseEnabled = true;
		nametext_submit.autoSize = flash.text.TextFieldAutoSize.NONE;
		nametext_submit.width = 500;
		nametext_submit.height = 48;
		nametext_submit.maxChars = 32;
		nametext_submit.defaultTextFormat = inputformat;
		grouptext_submit.selectable = true;
		grouptext_submit.mouseEnabled = true;
		grouptext_submit.autoSize = flash.text.TextFieldAutoSize.NONE;
		grouptext_submit.width = 500;
		grouptext_submit.height = 48;
		grouptext_submit.maxChars = 32;
		grouptext_submit.defaultTextFormat = inputformat;

		framessurvived.defaultTextFormat = centermenuformat;
		framessurvived.autoSize = flash.text.TextFieldAutoSize.NONE;
		framessurvived.width = 500;

		customgameinput = new Array();

		for (n in 0...6) // 1,000,000 games numbered 0 thru 999,999
			customgameinput.push(new CreepTextField());

		var count = 0;
		for (i in customgameinput)
		{
			i.selectable = false;
			i.defaultTextFormat = inputformat;
			i.autoSize = flash.text.TextFieldAutoSize.RIGHT;
			i.filters = [shadowfilter];

			i.text = "0";		

			i.x = FlashClient.xres/2 - 105 + (18 * count) ;
			i.y = FlashClient.yres/2 - 30 + 150 + 35;
			count+=1;
		}

		dailygametext.text = "New Game";
		customgametext.text = "Custom Game";
		creditstext.text = "Credits";
		highscorestext.text = "High Scores";
		back_hs.text = "Back";
		back_submit.text = "Cancel";
		submit_submit.text = "Send It!";
		header_submit.text = "High Score!";
		nametext.text = "Your Name:";
		grouptext.text = "(optional) Group tag:";

		framessurvived.text = "Frames survived: 0";

		nametext_submit.text = "Rick P.A.";
		grouptext_submit.text = "Anonymous";
		nametext_submit.type = flash.text.TextFieldType.INPUT;
		grouptext_submit.type = flash.text.TextFieldType.INPUT;

		// defaults

		FlashClient.musicon = true;
		FlashClient.sndvol = 1.0;
		FlashClient.qualitymode = 1;			

		// try to load the sharedobject settings

		try
		{
			settings = flash.net.SharedObject.getLocal("settings");
			
			if (settings.data.name!=null)
				nametext_submit.text = settings.data.name;
			if (settings.data.group!=null)
				grouptext_submit.text = settings.data.group;
			if (settings.data.music!=null)
				FlashClient.musicon = settings.data.music;
			if (settings.data.sndvol!=null)
				FlashClient.sndvol = settings.data.sndvol;
			if (settings.data.qualitymode!=null)
				FlashClient.qualitymode = settings.data.qualitymode;			
			reprtext();
		}
		catch (d : Dynamic) // something's up, go to defaults
		{
			/// note that when the game is compiled with changes,
			/// Flash may set up a new sharedobject for it. So
			/// if you want to test for settings retention make sure to
			/// run the game twice without recompiling!
			
			FlashClient.musicon = true;
			FlashClient.sndvol = 1.0;
			FlashClient.qualitymode = 1;			
			reprtext();
		}

		
		titlemenu.addChild(dailygametext);
		titlemenu.addChild(customgametext);
		titlemenu.addChild(creditstext);
		titlemenu.addChild(highscorestext);

		titlemenu.addChild(selectmusic);
		titlemenu.addChild(selectquality);
		titlemenu.addChild(selectsound);

		highscoremenu.addChild(back_hs);
		submitmenu.addChild(nametext);
		submitmenu.addChild(grouptext);
		submitmenu.addChild(back_submit);
		submitmenu.addChild(submit_submit);

		submitmenu.addChild(framessurvived);

		submitmenu.addChild(grouptext_submit);
		submitmenu.addChild(nametext_submit);
		submitmenu.addChild(header_submit);
		
		for (i in customgameinput)
			titlemenu.addChild(i);

		dailygametext.x = FlashClient.xres/2 - 195;
		dailygametext.y = FlashClient.yres/2 - 30;

		nametext.x = FlashClient.xres/2 - nametext.width/2;
		nametext.y = FlashClient.yres/2 - 150;

		header_submit.x = FlashClient.xres/2 - header_submit.width/2;
		header_submit.y = FlashClient.yres/2 - 260;

		grouptext.x = FlashClient.xres/2 - grouptext.width/2;
		grouptext.y = FlashClient.yres/2 + 15;

		nametext_submit.x = FlashClient.xres/2 - nametext_submit.width/2;
		nametext_submit.y = FlashClient.yres/2 - 100;

		grouptext_submit.x = FlashClient.xres/2 - grouptext_submit.width/2;
		grouptext_submit.y = FlashClient.yres/2 + 65;

		highscorestext.x = FlashClient.xres/2 - 70;
		highscorestext.y = FlashClient.yres/2 - 30 + 72;

		customgametext.x = FlashClient.xres/2 - 115;
		customgametext.y = FlashClient.yres/2 - 30 + 144;
		
		creditstext.x = FlashClient.xres/2 - 70;
		creditstext.y = FlashClient.yres/2 - 30 + 216;

		back_hs.x = FlashClient.xres/2 - 70;
		back_hs.y = FlashClient.yres/2 - 30 + 216;

		back_submit.x = FlashClient.xres/2 - 70;
		back_submit.y = FlashClient.yres/2 - 30 + 216;

		submit_submit.x = FlashClient.xres/2 - 115;
		submit_submit.y = FlashClient.yres/2 - 30 + 144;

		selectsound.x = FlashClient.xres/2 - 190;
		selectsound.y = FlashClient.yres/2 - 60;

		selectmusic.x = FlashClient.xres/2 - 10;
		selectmusic.y = FlashClient.yres/2 - 60;

		selectquality.x = FlashClient.xres/2 + 140;
		selectquality.y = FlashClient.yres/2 - 60;

		framessurvived.x = FlashClient.xres/2 - framessurvived.width/2;
		framessurvived.y = FlashClient.yres/2 - 200;

		buttondailygame.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				dailygame, false, 10);				
		buttoncustomgame.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				customgame, false, 10);				
		buttoncredits.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				credits, false, 10);				
		buttonhighscores.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				linktowebsite, false, 10);				
		buttonhs_titlemenu.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				switchtotitlemenu, false, 10);				
		buttonsubmit_titlemenu.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				switchtotitlemenu, false, 10);				
		buttonsubmit_submit.sprite.addEventListener(
				flash.events.MouseEvent.CLICK,
				sendscore, false, 10);				
		
		selectmusic.addEventListener(
				flash.events.MouseEvent.CLICK,
				togglemusic, false, 10);				
		selectsound.addEventListener(
				flash.events.MouseEvent.CLICK,
				changevolume, false, 10);				
		selectquality.addEventListener(
				flash.events.MouseEvent.CLICK,
				changequality, false, 10);				
		
		for (i in customgameinput)
		{
			var custominc = function (z : Dynamic)
			{
				SFX.interfacehi = true;
				var cur = i;
				var value = Std.parseInt(i.text);
				value += 1 ;
				if (value>9) value = 0;
				cur.text = Std.string(value);
			}
			i.addEventListener(
				flash.events.MouseEvent.CLICK,
				custominc, false, 10);		
		}

		var uielements : Array<Dynamic> = [
			container,
			buttoncredits.sprite,
			buttoncustomgame.sprite,
			buttondailygame.sprite,
			buttonhighscores.sprite,
			buttonhs_titlemenu.sprite,
			buttonsubmit_titlemenu.sprite,
			buttonsubmit_submit.sprite
			];
		for (ui in uielements)
			FlashClient.setui(ui);

		var titlelogo = new SpriteTitleLogo();
		titlemenu.addChild(titlelogo);
		titlelogo.x = FlashClient.xres/2 - (titlelogo.width/2 * 9);
		titlelogo.y = 80;
		titlelogo.scaleX = 9;
		titlelogo.scaleY = 9;
		
		titlemenu.visible = true;
		highscoremenu.visible = false;
		submitmenu.visible = false;
		
	}

	public function dailygame(?e: flash.events.MouseEvent)
	{
		buttondailygame.click();
		FlashClient.restartgame(FlashClient.todaysgame);
	}
	
	public function customgame(?e: flash.events.MouseEvent)
	{
		buttoncustomgame.click();
		var value = 0;
		value+=Std.parseInt(customgameinput[0].text)*100000;		
		value+=Std.parseInt(customgameinput[1].text)*10000;		
		value+=Std.parseInt(customgameinput[2].text)*1000;		
		value+=Std.parseInt(customgameinput[3].text)*100;		
		value+=Std.parseInt(customgameinput[4].text)*10;		
		value+=Std.parseInt(customgameinput[5].text)*1;
		FlashClient.restartgame(value);		
	}
	
	public function credits(?e: flash.events.MouseEvent)
	{
		buttoncredits.click();
		FlashClient.rollcredits();
	}

	public function linktowebsite(?e: flash.events.MouseEvent)
	{
		buttonhighscores.click();
		flash.Lib.getURL(new flash.net.URLRequest(
				"http://spacecreeps.com/creepsearchresult.php"), null);
	}
	
	public function switchtohighscores(?e: flash.events.MouseEvent)
	{
		buttonhighscores.click();
		titlemenu.visible = false;
		highscoremenu.visible = true;
		submitmenu.visible = false;		
	}

	public function switchtotitlemenu(?e: flash.events.MouseEvent)
	{
		buttonhs_titlemenu.click();
		buttonsubmit_titlemenu.click();
		titlemenu.visible = true;
		highscoremenu.visible = false;
		submitmenu.visible = false;		
	}

	public function switchtosubmit()
	{
		framessurvived.text ="Score: "+Std.string(
			rules.RulesAPI.g.framessurvived);
		buttonsubmit_submit.sprite.visible = true;
		titlemenu.visible = false;
		highscoremenu.visible = false;
		submitmenu.visible = true;		
	}

	public function sendscore(?e : flash.events.MouseEvent)
	{
		buttonsubmit_submit.sprite.visible = false;
		buttonsubmit_submit.click();
		FlashNetworkClient.uploadgame.request();
		savesettings();
	}

	public function togglemusic(?e : flash.events.MouseEvent)
	{

		SFX.interfacehi = true;

		if (FlashClient.musicon)
		{
			FlashClient.musicon = false;
		}
		else
		{
			FlashClient.musicon = true;			
		}
		
		reprtext();
	}
	
	public function changevolume(?e : flash.events.MouseEvent)
	{

		SFX.interfacehi = true;

		switch(FlashClient.sndvol)
		{
			default:
			FlashClient.sndvol = 0.0;
			case(0.0):
			FlashClient.sndvol = 0.25;
			case(0.25):			
			FlashClient.sndvol = 0.5;
			case(0.5):			
			FlashClient.sndvol = 0.75;
			case(0.75):			
			FlashClient.sndvol = 1.0;					
		}
		reprtext();
	}

	public function changequality(?e : flash.events.MouseEvent)
	{

		SFX.interfacehi = true;

		switch(FlashClient.qualitymode)
		{
			default:
			FlashClient.qualitymode = 0;
			case(0):
			FlashClient.qualitymode = 1;
			case(1):
			FlashClient.qualitymode = 2;
			case(2):
			FlashClient.qualitymode = 3;						
		}
		
		reprtext();
		
	}

	public function reprtext()
	{
		
		if (FlashClient.musicon)
			selectmusic.text = "Mus on";
		else
			selectmusic.text = "Mus off";

		switch (FlashClient.sndvol)
		{
			default:
				selectsound.text = "Snd off";
			case(0.25):
				selectsound.text = "Snd 25%";
			case(0.5):
				selectsound.text = "Snd 50%";
			case(0.75):
				selectsound.text = "Snd 75%";
			case(1.0):
				selectsound.text = "Snd 100%";

		}
		
		switch(FlashClient.qualitymode)
		{
			// the sound controls are on/off properties;
			// quality requires several state changes, so it's moved
			// over here for updates
			default:
			FlashClient.normalquality =
				flash.display.StageQuality.LOW;
			FlashClient.zoomquality =
				flash.display.StageQuality.LOW;
			selectquality.text = "Gfx Low";
			case(1):
			FlashClient.normalquality =
				flash.display.StageQuality.MEDIUM;
			FlashClient.zoomquality =
				flash.display.StageQuality.LOW;
			selectquality.text = "Gfx Med";
			case(2):
			FlashClient.normalquality =
				flash.display.StageQuality.HIGH;
			FlashClient.zoomquality =
				flash.display.StageQuality.MEDIUM;
			selectquality.text = "Gfx Hi";
			case(3):
			FlashClient.normalquality =
				flash.display.StageQuality.HIGH;
			FlashClient.zoomquality =
				flash.display.StageQuality.HIGH;
			selectquality.text = "Gfx V.Hi";
		}
		flash.Lib.current.stage.quality = FlashClient.normalquality;
		
		// save settings changes too
		
		savesettings();
				
	}

	public function savesettings()
	{

		try
		{
			settings.setProperty("name", nametext_submit.text);			
			settings.setProperty("group", grouptext_submit.text);			
			settings.setProperty("music", FlashClient.musicon);			
			settings.setProperty("sndvol", FlashClient.sndvol);			
			settings.setProperty("qualitymode", FlashClient.qualitymode);			
		}
		catch (d : Dynamic) {} // silent failure, who cares about settings?
		
	}
	
	
}

class Tips
{

	public static var xposition = 1; // 0 = mouse left, 1 = mouse right 
	public static inline var tip_leftx = 0;
	public static inline var tip_rightx = FlashClient.xres -
							 FlashClient.tipdialogue.width;
	public static inline var tip_y = 75;
	
	public static function tip_tower(?e : flash.events.MouseEvent)
	{
		FlashClient.tipdialoguetext.text=
	    "Place a tower next to your"+
		" existing fortress pieces. Towers gain experience and levels"+
		" when they fire, making them do more damage."+
		" Each tower costs "+
		Std.string(-1*rules.FortressData.cost_tower)+" points.";
	}
	public static function tip_line(?e : flash.events.MouseEvent)
	{
		FlashClient.tipdialoguetext.text=
		"Place a power line next to "+
		"your existing fortress pieces. Use lines to expand to the "+
		"energy resources on the map and gain more income. "+
		"They can be used as walls, "+
		"but each line you place costs one more than the last. "+
		"The current cost is "+Std.string(
			-1*rules.FortressData.cost_line+
		RulesAPI.g.numlinesplaced)+" points.";
	}
	public static function tip_target(?e : flash.events.MouseEvent)
	{
		FlashClient.tipdialoguetext.text=
		"Select a tower with this, "+
		"and then you will be able to choose an area."
		+" The tower automatically chooses a target within that area."
		+" All shots "+
		"take equal time to reach the target regardless of distance.";
	}
	public static function tip_zoom(?e : flash.events.MouseEvent)
	{
		FlashClient.tipdialoguetext.text=
		"Toggle between different zoom levels. You may also use the "+
		"mouse wheel to zoom in and out.";
	}
	public static function tip_pause(?e : flash.events.MouseEvent)
	{
		FlashClient.tipdialoguetext.text=
		"Pause and unpause the game. "+
		"You are allowed to place items while paused.";
	}
	public static function tip_slowdown(?e : flash.events.MouseEvent)
	{
		FlashClient.tipdialoguetext.text=
		"Slow the game down. The default speed is 30 milliseconds per"+
		" frame of action.";
	}
	public static function tip_fastforward(?e : flash.events.MouseEvent)
	{
		FlashClient.tipdialoguetext.text=
		"Speed the game up. The default speed is 30 milliseconds per"+
		" frame of action. The fastest speed is 1 millisecond.";
	}
	public static function tip_help(?e : flash.events.MouseEvent)
	{
		FlashClient.tipdialoguetext.text=
		"Toggle these help messages.\n\n"+
		"View each button and look over the map to learn how to play."+
		" When ready, unpause the game and watch the action."+
		" The number keys 1 through 9 are shortcut keys. \n"+
		"Current Game is #"+Std.string(RulesAPI.g.seed);
	}
	public static function tip_mainmenu(?e : flash.events.MouseEvent)
	{
		FlashClient.tipdialoguetext.text=
		"Toggle the main menu.";
	}
	public static function info_tile(?e : flash.events.MouseEvent)
	{
		var result : String;

		var testpt = FlashClient.tilemap.screencoordtogamecoord(
			flash.Lib.current.stage.mouseX,
			flash.Lib.current.stage.mouseY);
		
		if (rules.GameMap.inbounds(testpt.x,testpt.y))
		{
		
		var tile : rules.GameTile = 
			RulesAPI.g.m.tiles[testpt.x][testpt.y];
		
		switch(tile.terrain)
		{
			case(rules.GameTile.ter_space):
				result = "Space. Creeps come in from here. They"+
				" cannot attack from space, but your towers can fire "+
				"into it. Nothing can be built here. ";
			case(rules.GameTile.ter_ground):
				result = "Ground. Safe for building. ";
			case(rules.GameTile.ter_energy):
				result = "Energy. Each frame of gameplay, you gain"+
				" 4 resource points."+
				" Placing a fortress piece over energy"+
				" increases this income by"+
				" 1 per energy tile. ";
		}

		if (tile.creeps.length>0)
		{
			if (tile.creeps.length==1)
			{
				result+="The creep here has "+
					Std.string(tile.creeps[0].health)+
					" HP.";
			}
			else
			{
				result+="There are "+Std.string(tile.creeps.length)+
				" creeps here. ";
				var avg = 0;
				for (c in tile.creeps)
				{
					avg+=c.health;
				}
				avg = Std.int(avg/tile.creeps.length);
				result+="The creeps have an average of "+
					Std.string(avg)+" health.";
			}
		}
		
		if (tile.fort!=null)
		{
			switch(tile.fort.style)
			{
				case(rules.FortressData.fort_line):
					result+="There is an energy line here. ";
				case(rules.FortressData.fort_base):
					result+="Your base of operations is here. If it is"
					+" lost, the game ends. ";
				case(rules.FortressData.fort_tower):
					result+="A level "+Std.string
						(tile.fort.shooter.level)
					+" tower is here. It does "+
					Std.string(tile.fort.shooter.dmg)
					+" damage per shot. It needs "+
					Std.string(rules.Shooter.expfornextlevel -
					tile.fort.shooter.experience)+
					" more experience for an upgrade. " +
					"Damage splashes to all"+
					" creeps in the affected tile. "; 
			}
			result+="It has "+Std.string(tile.fort.health)+"/"+
				Std.string(rules.FortressData.fort_maxhealth)+
				" health. ";
		}
		
		FlashClient.tipdialoguetext.text=
		result;		
	}
	}
	public static function up()
	{
		if (xposition==0)
		{
			if (flash.Lib.current.mouseX>(5/8)*FlashClient.xres)
			{
				xposition = 1;
				FlashClient.tipdialogue.x = tip_leftx;
			}
		}
		else
		{
			if (flash.Lib.current.mouseX<(3/8)*FlashClient.xres)
			{
				xposition = 0;
				FlashClient.tipdialogue.x = tip_rightx;
			}
		}
		
		
	}
}

class GraphicalTileMap
{

	inline public static var tilexsize = 100.0;
	inline public static var tileysize = 86.67;
	
	public var scalesize : Float;
	private var lastscalesize : Float;
	public var destscalesize : Float;

	public var camx : Float;
	public var camy : Float;

	public var camtx : Int;
	public var camty : Int;

	public var camdestx : Float;
	public var camdesty : Float;
	
	public var camspeed : Float; // speed in percentage distance per
								 // frame

	public var lockx : Float; // lock scrolling when mouse out of frame
	public var locky : Float;

	public var scrollwait : Int;

	public var truecoordinates : Array<Array<lib3f.Point>>;
	// truecoordinates maps the map coordinates to display
	// coordinates. No hard work translating them on-the-fly,
	// other than when tile-picking.

	public var spr : flash.display.Sprite;
	public var forts : FortSpriteContainer;
	public var creeps : CreepSpriteContainer;
	public var waves : WaveSpriteContainer;
	public var shots : ShotSpriteContainer;
	public var targets : TargetSpriteContainer;
	public var hexes : flash.display.Sprite;

	public var picker : SpriteCursor;

	public function new()
	{
		scrollwait = 0;
		scalesize = 0.4;
		gentruecoordinates();
		camspeed = 0.04;
		spr = new flash.display.Sprite();
		hexes = new flash.display.Sprite();
		waves = new WaveSpriteContainer();
		forts = new FortSpriteContainer();
		creeps = new CreepSpriteContainer();
		shots = new ShotSpriteContainer();
		targets = new TargetSpriteContainer();
		setcam(rules.GameMap.mapxcenter,rules.GameMap.mapycenter);
		camx = camdestx;
		camy = camdesty;
		lastscalesize = scalesize;
		destscalesize = scalesize;

		spr.addChild(hexes);
		spr.addChild(waves);
		spr.addChild(creeps);
		spr.addChild(forts);
		spr.addChild(targets);
		spr.addChild(shots);

		picker = new SpriteCursor();

		spr.addChild(picker);
		spr.mouseChildren = false;
		setoutofzoom();

	}

	public inline function mouseLockCheck(inx : Float,
										  iny : Float) : Bool
	{
		if (!(lockx == inx &&
			locky == iny))
		{ // reset the lock when we're in bounds
			lockx = -1;
			locky = -1;
			return false;
		}
		else		
		return true;
		
	} 

	public function mouseLeave(e : flash.events.Event)
	{
		lockx = flash.Lib.current.stage.mouseX;
		locky = flash.Lib.current.stage.mouseY;
	}

	public function picktile(inx : Float, iny : Float)
	{		
		var tmap = FlashClient.tilemap;
		
		if (mouseLockCheck(inx, iny))
		{
			inx = FlashClient.xres/2;
			iny = FlashClient.yres/2;
			tmap.picker.visible = false;
		}
		else
		{
			tmap.picker.visible = true;
		}
		
		var result = tmap.screencoordtogamecoord(
			inx, iny);
			
			
		if(rules.GameMap.inbounds(result.x,result.y))
		{

		// move cursor

			var true = tmap.truecoordinates
				[result.x][result.y];
			tmap.picker.x = true.x;
			tmap.picker.y = true.y;
		
		// scroll

			scrollwait -=1;
			if (scrollwait<0) scrollwait=0;
					
			if ((inx<(FlashClient.xres/11) ||
			    iny<(FlashClient.yres/11) ||
			    inx>(10*FlashClient.xres/11) ||
			    iny>(10*FlashClient.yres/11)) &&
			    scrollwait <= 0)
			{
				// scroll only one tile at a time
				var distx = tmap.camtx-result.x;
				var disty = tmap.camty-result.y;
				var movex = Std.int(distx/Math.abs(distx));
				var movey = Std.int(disty/Math.abs(disty));
		    	tmap.setcam(tmap.camtx-movex,tmap.camty-movey);
		    	scrollwait = 5;
			}
		}
	}

	public function screencoordtogamecoord(x : Float, y : Float) : 
		lib3f.IntPoint
	{
		// mainly meant for tile picking
		
		var truetxsize = tilexsize * 3/4 * scalesize;
		var truetysize = tileysize * scalesize;

		var scaledcamx = camx * scalesize;
		var scaledcamy = camy * scalesize;

		x = (x)+scaledcamx - FlashClient.xres/2;
		y = (y)+scaledcamy - FlashClient.yres/2;

		var truex = x - (1/8 * truetxsize);
		var truey = y ;

		var endx = Std.int(truex/truetxsize);
		
		var endy : Int;
		if (endx/2 == Std.int(endx/2))
		{
			endy = Std.int((truey-truetysize/2)/truetysize);
		}
		else
			endy = Std.int(truey/truetysize);

		return new lib3f.IntPoint(Std.int(endx),Std.int(endy));
	}

	private function gentruecoordinates()
	{
		truecoordinates = new Array();
		var xoffset = 0.0;
		for (x in 0...rules.GameMap.mapxsize)
		{
			var yoffset = 0.0;
			if (x/2 == Std.int(x/2))
			{
				yoffset = tileysize/2;
			}
			truecoordinates.push(new Array());
			for (y in 0...rules.GameMap.mapysize)
			{
				
				truecoordinates[x].push(new 
					lib3f.Point(xoffset, y*tileysize + 
								yoffset));
			}
			xoffset+=(3/4) * tilexsize;
		}
	}

	public function tilemapgen()
	{
		// note:
		// while the game can potentially send terrain change messages,
		// right now they aren't used or supported anywhere
		
		for (n in 0...hexes.numChildren)
			hexes.removeChildAt(0);

		var tiles = RulesAPI.g.m.tiles;

		for (x in 0...rules.GameMap.mapxsize)
		{
			for (y in 0...rules.GameMap.mapysize)
			{
				var cur = tiles[x][y];
				
				if (cur.terrain!=rules.GameTile.ter_space)
				{
					var newhex = new SpriteHex();
					newhex.x = truecoordinates[x][y].x;
					newhex.y = truecoordinates[x][y].y;
					hexes.addChild(newhex);
				}
				if (cur.terrain==rules.GameTile.ter_energy)
				{
					var newegy = new SpriteEnergy();
					newegy.x = truecoordinates[x][y].x;
					newegy.y = truecoordinates[x][y].y;
					hexes.addChild(newegy);					
				}
			}
		}
		upcam();
	}
		
	public function upcam()
	{

		var xdist = camdestx - camx;
		var ydist = camdesty - camy;

		var camyspeed = ydist * camspeed;	
		var camxspeed = xdist * camspeed;
		
		camx += camxspeed;			
		camy += camyspeed;			
		
		if (destscalesize!=scalesize)
		{
			var scalechange = (destscalesize-scalesize)*0.2;
			if (Math.abs(scalechange)<0.0004)
				scalechange = destscalesize-scalesize;
			scalesize += scalechange;
		}
		
		spr.transform.matrix = new
					flash.geom.Matrix(scalesize,0,0,scalesize,
							 -camx * scalesize +FlashClient.xres/2,
							 -camy * scalesize +FlashClient.yres/2);
		
		if (scalesize==lastscalesize) // while zooming, 
									  // everything gets redrawn,
									  // caching fails.
		{
			if (hexes.cacheAsBitmap==false)
			{
				setoutofzoom();
			}
		}
		else
		{
			if (hexes.cacheAsBitmap==true)
			{
				setinzoom();
			}
		}
		lastscalesize = scalesize;
	}

	public function setoutofzoom()
	{
		flash.Lib.current.stage.quality = FlashClient.normalquality;
		hexes.cacheAsBitmap = true;
		forts.cacheAsBitmap = true;
		waves.cacheAsBitmap = true;
		targets.cacheAsBitmap = true;
		for (n in creeps.spritehash)
			n.cacheAsBitmap = true;
		for (n in shots.spritehash)
			n.cacheAsBitmap = true;
		waves.cacheAsBitmap = true;
		if (FlashClient.clickmode==FlashClient.click_selecttower ||
		    FlashClient.clickmode==FlashClient.click_selecttarget)
			targets.visible = true;
		else
			targets.visible = false;
		creeps.visible = true;
		shots.visible = true;
	}

	public function setinzoom()
	{
		flash.Lib.current.stage.quality = FlashClient.zoomquality;
		hexes.cacheAsBitmap = false;
		forts.cacheAsBitmap = false;
		waves.cacheAsBitmap = false;
		targets.visible = false;
		creeps.visible = false;
		shots.visible = false;
	}
	
	public function setcam(tx : Int, ty : Int)
	{
		if (rules.GameMap.inbounds(tx,ty))
		{
			camtx = tx;
			camty = ty;
			var true = truecoordinates[tx][ty];
			camdestx = true.x + tilexsize / 2;
			camdesty = true.y + tileysize / 2;
		}
	}
	
}

class FortSpriteContainer extends flash.display.Sprite
{
	
	public var spritehash : IntHash<SpriteTower>;
	public var linehash : IntHash<flash.display.Sprite>;
	public var parenthash : IntHash<rules.FortressData>;
	public var healthhash : IntHash<flash.display.Sprite>;
	
	public function new()
	{
		spritehash = new IntHash();
		parenthash = new IntHash();
		linehash = new IntHash();
		healthhash = new IntHash();
		super();
	}
	
	public function addfort(fort : rules.FortressData, id : Int)
	{
		parenthash.set(id, fort); // we need this for neighbor
									  // calcs
		var newfort : Dynamic;
		if ((fort.style)==rules.FortressData.fort_tower)
		{
			newfort = new SpriteTower();
			newfort.rotation = Math.random()*360;
		}
		else if ((fort.style)==rules.FortressData.fort_base)
		{
			newfort = new SpriteBase();
		}
		else 
		{
			newfort = new SpriteLine();
		}
		spritehash.set(id, newfort);
		lineupdate(fort,true);
		var truepoint = 
			FlashClient.tilemap.truecoordinates[fort.x][fort.y];
		newfort.x = truepoint.x + GraphicalTileMap.tilexsize /2;
		newfort.y = truepoint.y + GraphicalTileMap.tileysize /2;
		this.addChild(newfort);
		
		var newhealth = new flash.display.Sprite();
		healthhash.set(id, newhealth);		
		newhealth.visible = false;
		newfort.addChild(newhealth);

	}

	public function healthupdate(obj : rules.FortressData)
	{
		var myhealth = healthhash.get(obj.id);
		var curhealth = obj.health;
		if (curhealth < 
			rules.FortressData.fort_maxhealth)
		{
			myhealth.visible = true;
			myhealth.graphics.clear();
			myhealth.graphics.lineStyle(6,0xFF0000);
			myhealth.graphics.moveTo(-30,0);			
			myhealth.graphics.lineTo(-30+60*
				(1-(curhealth/rules.FortressData.fort_maxhealth))
				,0);			
		}	
		else
		{
			myhealth.visible = false;
		}
		
	}

	public function lineupdate(fort : rules.FortressData, first :
								Bool)
	{
		var cur = RulesAPI.g.m.tiles[fort.x][fort.y];
		for (n in cur.neighbors)
			if (n.fort!=null)
			{
				if (first)
					lineupdate(n.fort,false); 
						// ^^ don't recurse indefinitely
			}
					
		if (fort.style==rules.FortressData.fort_line)
		{
		var id = fort.id;
		var parentsprite : flash.display.Sprite = linehash.get(id);
		if (parentsprite==null)
		{
			parentsprite = new flash.display.Sprite();
			this.addChild(parentsprite);
			linehash.set(id, parentsprite);
		}
		else
		{
			for (c in 0...parentsprite.numChildren)
				parentsprite.removeChildAt(0);
		}
		for (n in cur.neighbors)
			if (n.fort!=null)
			{
				var lshape = new flash.display.Shape();
				var starttrue = FlashClient.tilemap.truecoordinates
								[fort.x][fort.y];
				var endtrue = FlashClient.tilemap.truecoordinates
								[n.x][n.y];
				var distx = endtrue.x-starttrue.x;
				var disty = endtrue.y-starttrue.y;
				lshape.graphics.lineStyle(5,0xCCCC22);
				lshape.graphics.moveTo(
					starttrue.x+GraphicalTileMap.tilexsize/2,
					starttrue.y+GraphicalTileMap.tileysize/2);
				lshape.graphics.lineTo(
					starttrue.x+distx/2+GraphicalTileMap.tilexsize/2,
					starttrue.y+disty/2+GraphicalTileMap.tilexsize/2);
				parentsprite.addChild(lshape);
			}
		}
	}
	
	public function killfort(fort : rules.FortressData)
	{
		var spr = this.spritehash.get(fort.id);
		this.removeChild(spr);
		spr = null;
		this.spritehash.remove(fort.id);
		this.parenthash.remove(fort.id);
		this.healthhash.remove(fort.id);
		try
		{
			var lspr = this.linehash.get(fort.id);
			this.removeChild(lspr);
			lspr = null;
		}
		catch (x: Dynamic) {}
	}

	public function rotatefort(shooter : rules.Shooter, 
							   shot : rules.Shot)
	{
		var spr = this.spritehash.get(shooter.parent.id);
		var truestart = FlashClient.tilemap.truecoordinates
			[shot.startx][shot.starty];
		var trueend = FlashClient.tilemap.truecoordinates
			[shot.endx][shot.endy];			
		var raded = lib3f.VecAngle.vecangle(trueend.x-truestart.x,
										  trueend.y-truestart.y);
		spr.rotation = raded * 360 / (3.141496 * 2);
	}
	
}

class WaveSpriteContainer extends flash.display.Sprite
{
	
	public var spritehash : IntHash<flash.display.Sprite>;
	
	public function new()
	{
		spritehash = new IntHash();
		super();
	}
	
	public function addwave(wave : rules.Wave)
	{
		var newwave = new flash.display.Sprite();
		for (cur in wave.path)
		{
			var pathmarker = new flash.display.Shape();
			pathmarker.graphics.clear();
			pathmarker.graphics.moveTo(0,0);
			var truepoint = FlashClient.tilemap.truecoordinates
					[cur.x][cur.y];
			var tx = truepoint.x + 
				(GraphicalTileMap.tilexsize * 3 /8);
			var ty = truepoint.y + 
				(GraphicalTileMap.tileysize * 3 /8);
			pathmarker.graphics.beginFill(0x441111);
			pathmarker.graphics.lineStyle(1, 0x882222);
			pathmarker.graphics.drawRect(0, 0,
									  GraphicalTileMap.tilexsize/4,
									  GraphicalTileMap.tileysize/4);
			pathmarker.graphics.endFill();
			newwave.addChild(pathmarker);
			pathmarker.x = tx;
			pathmarker.y = ty;
		}
		spritehash.set(wave.num, newwave);
		this.addChild(newwave);
	}
	
	public function endwave(wave : rules.Wave)
	{
		var spr = this.spritehash.get(wave.num);
		for (n in 0...spr.numChildren)
			spr.removeChildAt(0);
		this.removeChild(spr);
		spr = null;
		this.spritehash.remove(wave.num);
	}
	
}

class CreepSpriteContainer extends flash.display.Sprite
{
	
	public var spritehash : IntHash<flash.display.Sprite>;
	public var healthhash : IntHash<flash.display.Sprite>;
	
	public function new()
	{
		spritehash = new IntHash();
		healthhash = new IntHash();
		super();
	}
	
	public function addcreep(id : Int, creep : rules.Creep)
	{
		var newcreep = new flash.display.Sprite();
		newcreep.graphics.clear();
		newcreep.graphics.moveTo(0,0);
		var truepoint = FlashClient.tilemap.truecoordinates
				[creep.x][creep.y];
		var tx = truepoint.x;
		tx += GraphicalTileMap.tilexsize /4;
		var ty = truepoint.y; 
		ty += GraphicalTileMap.tileysize /4;
		newcreep.graphics.beginFill(0xEE2222);
		newcreep.graphics.lineStyle(1);
		newcreep.graphics.moveTo(GraphicalTileMap.tilexsize/-4
							     ,GraphicalTileMap.tileysize/-4);
		newcreep.graphics.lineTo(GraphicalTileMap.tilexsize/4,
								 0);
		newcreep.graphics.lineTo(GraphicalTileMap.tilexsize/-4
							     ,GraphicalTileMap.tileysize/4);
		newcreep.graphics.lineTo(GraphicalTileMap.tilexsize/-4
							     ,GraphicalTileMap.tileysize/-4);
		newcreep.graphics.endFill();
		newcreep.cacheAsBitmap = true;
		this.addChild(newcreep);
		newcreep.x = tx;
		newcreep.y = ty;
		spritehash.set(creep.id, newcreep);

		var newhealth = new flash.display.Sprite();
		healthhash.set(creep.id, newhealth);		
		newhealth.visible = false;
		newcreep.addChild(newhealth);
	}
	
	public function killcreep(creep : rules.Creep)
	{
		var spr = this.spritehash.get(creep.id);
		this.removeChild(spr);
		spr = null;
		this.spritehash.remove(creep.id);
		this.healthhash.remove(creep.id);
	}

	public function healthupdate(obj : rules.Creep)
	{
		var myhealth = healthhash.get(obj.id);
		var curhealth = obj.health;
		if (curhealth < 
			obj.maxhealth)
		{
			myhealth.visible = true;
			myhealth.graphics.clear();
			myhealth.graphics.lineStyle(6,0x00FF00);
			myhealth.graphics.moveTo(GraphicalTileMap.tilexsize/-4,0);			
			myhealth.graphics.lineTo(GraphicalTileMap.tilexsize/2*
				(1-(curhealth/obj.maxhealth))
				,0);			
		}	
		else
		{
			myhealth.visible = false;
		}
		
	}

	public function move(creep : rules.Creep, pct : Float)
	{
		var truepoint = FlashClient.tilemap.truecoordinates
				[creep.x][creep.y];
		var dpoint = FlashClient.tilemap.truecoordinates
				[creep.destx][creep.desty];
		var tx = (truepoint.x - (truepoint.x - dpoint.x)*pct);
		tx += GraphicalTileMap.tilexsize /2;
		var ty = (truepoint.y - (truepoint.y - dpoint.y)*pct); 
		ty += GraphicalTileMap.tileysize /2;
		var spr = spritehash.get(creep.id);
		spr.x = tx;
		spr.y = ty;
		
		var raded = lib3f.VecAngle.vecangle(dpoint.x-truepoint.x,
										  dpoint.y-truepoint.y);
		spr.rotation = raded * 360 / (3.141496 * 2);
		
	}
	
}

class ShotSpriteContainer extends flash.display.Sprite
{
	
	public var spritehash : IntHash<SpriteShot>;
	
	public function new()
	{
		spritehash = new IntHash();
		super();
	}
	
	public function addshot(shot : rules.Shot)
	{
		var newshot = new SpriteShot();
		this.addChild(newshot);
		var truepoint = FlashClient.tilemap.truecoordinates
				[shot.startx][shot.starty];
		var tx = truepoint.x;
		var ty = truepoint.y; 
		newshot.x = tx + GraphicalTileMap.tilexsize/2;
		newshot.y = ty + GraphicalTileMap.tileysize/2;
		newshot.cacheAsBitmap = true;
		newshot.visible = false;
		spritehash.set(shot.id, newshot);
	}
	
	public function killshot(shotid : Int)
	{
		var spr = this.spritehash.get(shotid);
		this.removeChild(spr);
		spr = null;
		this.spritehash.remove(shotid);
	}

	public function move(shot : rules.Shot, pct : Float)
	{
		var truepoint = FlashClient.tilemap.truecoordinates
				[shot.startx][shot.starty];
		var dpoint = FlashClient.tilemap.truecoordinates
				[shot.endx][shot.endy];
		var tx = (truepoint.x - (truepoint.x - dpoint.x)*pct);
		tx += GraphicalTileMap.tilexsize /2;
		var ty = (truepoint.y - (truepoint.y - dpoint.y)*pct); 
		ty += GraphicalTileMap.tileysize /2;
		var spr = spritehash.get(shot.id);
		if (pct>0.1)
		spr.visible = true;
		spr.x = tx;
		spr.y = ty;
	}
	
}

class TargetSpriteContainer extends flash.display.Sprite
{
	
	public var spritehash : IntHash<Array<flash.display.Sprite>>;
	public var cursorattachedtarget : flash.display.Sprite;
	public var heatmapoverlay : flash.display.Sprite;
	public var pointeroverlay : flash.display.Sprite;

	public function new()
	{
		super();
		spritehash = new IntHash();
		cursorattachedtarget = new flash.display.Sprite();
		heatmapoverlay = new flash.display.Sprite();
		pointeroverlay = new flash.display.Sprite();
		this.addChild(heatmapoverlay);
		this.addChild(pointeroverlay);
		this.addChild(cursorattachedtarget);
	}

	private function drawhex(targspr : Array<flash.display.Sprite>, 
						  targshooter : rules.Shooter)
	{
		var truec = FlashClient.tilemap.truecoordinates;
		for (t in targshooter.target)
			{
				var newp = truec[t.x][t.y];
				var curhex = new SpriteTarget();
				curhex.x = newp.x;
				curhex.y = newp.y;
				curhex.blendMode = flash.display.BlendMode.DIFFERENCE;
				curhex.alpha = Math.min(0.3, 
						0.15 + 0.0005 * targshooter.level);
				targspr[0].addChild(curhex);
			}		

		var targc = targshooter.target[0]; // the first = the center
		var truetargc = truec[targc.x][targc.y];
		var trueshoot = truec[targshooter.x][targshooter.y];
		var shootx = trueshoot.x;
		var shooty = trueshoot.y;
		var targx = truetargc.x;
		var targy = truetargc.y;
		var distx = targx - shootx;
		var disty = targy - shooty;

		// this one is a "baked" picture outlining the area
		// around the center of the target

		var bounds = new SpriteTargetOutline();

		bounds.x = targx - bounds.width/2 + 
			GraphicalTileMap.tilexsize/2;
		bounds.y = targy - bounds.height/2 +
			GraphicalTileMap.tileysize/2;

		targspr[0].addChild(bounds);

		// overlay the "pointer" on top of that		
		// we get the tangent of the vector so that we can make a
		// pretty triangle
		
		var fulldist = lib3f.Dist.float(0,distx,0,disty);
		
		var vecx : Float;
		if (distx==0)
			vecx = 0;
		else
		 	vecx = distx/fulldist;
		var vecy : Float;
		if (disty==0)
			vecy = 0;
		else
		 	vecy = disty/fulldist;
		var tanx1 = -vecy;
		var tany1 = vecx;
		var tanx2 = vecy;
		var tany2 = -vecx;

		var tri = new flash.display.Shape();
		tri.graphics.clear();
		tri.graphics.beginFill(0xEEEEEE,0.8);
		tri.graphics.lineStyle(1);
		tri.graphics.moveTo(targx+GraphicalTileMap.tilexsize/2,
							targy+GraphicalTileMap.tileysize/2);
		tri.graphics.lineTo(shootx+GraphicalTileMap.tilexsize/2
							+tanx1*4,
							shooty+GraphicalTileMap.tileysize/2
							+tany1*4);
		tri.graphics.lineTo(shootx+GraphicalTileMap.tilexsize/2
							+tanx2*4,
							shooty+GraphicalTileMap.tileysize/2
							+tany2*4);
		tri.graphics.lineTo(targx+GraphicalTileMap.tilexsize/2,
							targy+GraphicalTileMap.tileysize/2);
		tri.graphics.endFill();

		targspr[1].addChild(tri);


	}

	public function drawrangehex( 
						  targshooter : rules.Shooter)
	{
		for (n in 0...cursorattachedtarget.numChildren)
			cursorattachedtarget.removeChildAt(0);
		var truec = FlashClient.tilemap.truecoordinates;
		for (t in targshooter.validtargets)
			{
				var newp = truec[t.x][t.y];
				var curhex = new SpriteTargetRange();
				curhex.x = newp.x;
				curhex.y = newp.y;
				curhex.alpha = 0.7;
				cursorattachedtarget.addChild(curhex);
			}
	}
	
	public function add(shooter : rules.Shooter)
	{
		var newheat : flash.display.Sprite = 
					  new flash.display.Sprite();
		var newpoint : flash.display.Sprite = 
					  new flash.display.Sprite();
		this.drawhex([newheat,newpoint], shooter);
		heatmapoverlay.addChild(newheat);
		pointeroverlay.addChild(newpoint);
		spritehash.set(shooter.parent.id, [newheat, newpoint]);
		
		
	}

	public function update(shooter : rules.Shooter)
	{
		var targ = spritehash.get(shooter.parent.id);
		for (n in 0...targ[0].numChildren)
			targ[0].removeChildAt(0);
		for (n in 0...targ[1].numChildren)
			targ[1].removeChildAt(0);
		this.drawhex(targ, shooter);
	}

	public function remove(shooter : rules.Shooter)
	{
		var targ = spritehash.get(shooter.parent.id);
		for (n in 0...targ[0].numChildren)
			targ[0].removeChildAt(0);
		for (n in 0...targ[1].numChildren)
			targ[1].removeChildAt(0);
		heatmapoverlay.removeChild(targ[0]);
		pointeroverlay.removeChild(targ[1]);
		spritehash.remove(shooter.parent.id);
	}
}

class Background extends flash.display.Sprite
{

	// at some point I would like this to scroll in a parallax fashion,
	// independent of the scaling tilemap. 
	
	//// also, using a lot of stars really slows down the rendering!

	public function new()
	{
		super();
		var g = this.graphics;
		g.beginFill(0x000000);
		g.drawRect(0,0,FlashClient.xres,FlashClient.yres);
		g.endFill();

		var xres = FlashClient.xres;
		var yres = FlashClient.yres;

		for (n in 0...200)
		{
			addstar(Math.random()*(xres),
					Math.random()*(yres));
		}
		
		this.cacheAsBitmap = true;
	}
	
	public inline function addstar(x : Float, y : Float)
			: flash.display.Shape
	{
		var newshape = new flash.display.Shape(); 
		this.addChild(newshape);
		newshape.x = x;
		newshape.y = y;
		var scale = 1.25+(1*Math.random());
		newshape.scaleX = scale;
		newshape.scaleY = scale;
		var g = newshape.graphics;
		var matrix = new flash.geom.Matrix();
		matrix.createGradientBox(7*3.14,7*3.14,0,-2.5*3.14,-2.5*3.14);
		g.beginGradientFill(
			flash.display.GradientType.RADIAL,
			[Std.int(Math.random()*0xFFFFFF),
			Std.int(Math.random()*0xFFFFFF),
			Std.int(Math.random()*0xFFFFFF)],[1,Math.random()/10,0],
			[0x00,0x11,0x40],
			matrix
			);
		g.drawCircle(0,0,10);
		g.endFill();
		return newshape;
	}
	
}

class ClickyButton
{
	public var sprite : Dynamic;
	public var remainingwait : Int;
	inline public static var clickwait = 2;
	public function new(sprite : Dynamic) : Void
	{
		this.sprite = sprite;
		remainingwait = 0;
		this.sprite.gotoAndStop("unpressed");
		this.sprite.filters=[];
	}	
	public function click() : Void
	{
		SFX.interfacehi = true;
		remainingwait = clickwait;
		this.sprite.gotoAndStop("pressed");		
		this.sprite.filters=[FlashClient.glowfilter];
	}
	public function up() : Void
	{
		remainingwait-=1;
		if (remainingwait<0)
		{
			remainingwait=0;
			this.sprite.gotoAndStop("unpressed");
			this.sprite.filters=[];
		}
	}
}

class TogglyButton
{
	public var sprite : Dynamic;
	public var on : Bool;
	public function new(sprite : Dynamic) : Void
	{
		this.sprite = sprite;
		this.sprite.gotoAndStop("unpressed");
		this.on = false;
	}	
	public function click() : Void
	{
		on = !on;
		if (on)
		{
			SFX.interfacehi = true;
			this.sprite.gotoAndStop("pressed");		
			this.sprite.filters = [FlashClient.glowfilter];
		}
		else
		{
			SFX.interfacelo = true;
			this.sprite.gotoAndStop("unpressed");		
			this.sprite.filters = [];
		}
	}
}

extern class SpriteTitleLogo extends flash.display.Sprite {}
extern class SpriteHex extends flash.display.Sprite {}
extern class SpriteCursor extends flash.display.Sprite {}
extern class SpriteEnergy extends flash.display.Sprite {}
extern class SpriteTarget extends flash.display.Sprite {}
extern class SpriteTower extends flash.display.Sprite {}
extern class SpriteLine extends flash.display.Sprite {}
extern class SpriteBase extends flash.display.Sprite {}
extern class SpriteShot extends flash.display.Sprite {}
extern class SpriteDialogue extends flash.display.Sprite {}
extern class SpriteEnergyCounter extends flash.display.Sprite {}
extern class SpriteWaveCounter extends flash.display.Sprite {}
extern class SpriteTargetRange extends flash.display.Sprite {}
extern class SpriteTargetOutline extends flash.display.Sprite {}
extern class SpriteButtonPlaceTower extends flash.display.MovieClip {}
extern class SpriteButtonPlaceLine extends flash.display.MovieClip {}
extern class SpriteButtonSelectTower extends flash.display.MovieClip {}
extern class SpriteButtonZoomToggle extends flash.display.MovieClip {}
extern class SpriteButtonPause extends flash.display.MovieClip {}
extern class SpriteButtonFastForward extends flash.display.MovieClip {}
extern class SpriteButtonSlowDown extends flash.display.MovieClip {}
extern class SpriteButtonHelp extends flash.display.MovieClip {}
extern class SpriteButtonMainMenu extends flash.display.MovieClip {}
extern class SpriteButtonBlank extends flash.display.MovieClip {}
extern class SpriteForwardButton extends flash.display.MovieClip {}
